package com.googlecode.connectlet.crypto.rsa.provider;

import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactorySpi;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

public final class RSAKeyFactory extends KeyFactorySpi {
	@Override
	protected PublicKey engineGeneratePublic(KeySpec keySpec)
			throws InvalidKeySpecException {
		if (keySpec instanceof CompactKeySpec) {
			return ((CompactKeySpec) keySpec).getPublicKey();
		}
		throw new InvalidKeySpecException("CompactKeySpec required");
	}

	@Override
	protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
			throws InvalidKeySpecException {
		if (keySpec instanceof CompactKeySpec) {
			return ((CompactKeySpec) keySpec).getPrivateKey();
		}
		throw new InvalidKeySpecException("CompactKeySpec required");
	}

	@Override
	protected Key engineTranslateKey(Key key) throws InvalidKeyException {
		if (key instanceof RSAPublicKey) {
			return new RSAPublicKeyImpl(((RSAPublicKey) key).getModulus());
		} else if (key instanceof RSAPrivateCrtKey) {
			RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) key;
			return new RSAPrivateCrtKeyImpl(privateKey.getPrimeP(),
					privateKey.getPrimeQ(), privateKey.getPrimeExponentP(),
					privateKey.getPrimeExponentQ(), privateKey.getCrtCoefficient());
		} else {
			throw new InvalidKeyException("RSAPublicKey or RSAPrivateCrtKey required");
		}
	}

	@Override
	protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec) throws InvalidKeySpecException {
		if (keySpec.isAssignableFrom(CompactKeySpec.class)) {
			if (key instanceof RSAPublicKey) {
				return keySpec.cast(new CompactKeySpec((RSAPublicKey) key));
			} else if (key instanceof RSAPrivateCrtKey) {
				return keySpec.cast(new CompactKeySpec((RSAPrivateCrtKey) key));
			} else {
				throw new InvalidKeySpecException("RSAPublicKey or RSAPrivateCrtKey required");
			}
		}
		throw new InvalidKeySpecException("keySpec must be CompactKeySpec");
	}
}